﻿using System.Collections.Generic;
using System.Linq;
using Ahresty.Entity;

namespace Ahresty.Algorithms
{
    /// <summary>
    /// 薄板形状の要素を操作するクラス
    /// </summary>
    internal class BoardHandler
    {
        #region Methods

        /// <summary>
        /// 指定の材料データから薄板形状の要素を探し出す
        /// </summary>
        internal static Board[] LookForBoards(IN3DData in3dData)
        {
            List<Board> boards = new List<Board>();

            for (short z = 1; z < in3dData.MaxZ - 1; z++)
                for (short y = 1; y < in3dData.MaxY - 1; y++)
                    for (short x = 1; x < in3dData.MaxX - 1; x++)
                    {
                        Dimension2D dimension;
                        if (BoardHandler.CheckBoard(in3dData, x, y, z, out dimension))
                            boards.Add(new Board { X = x, Y = y, Z = z, Dimension = dimension, });
                    }

            return boards.ToArray();
        }

        /// <summary>
        /// 指定の材料データから薄板形状の要素を探し出す（要素の数のみを返す）
        /// </summary>
        internal static int LookForBoardCount(IN3DData in3dData)
        {
            int result = 0;

            for (short z = 1; z < in3dData.MaxZ - 1; z++)
                for (short y = 1; y < in3dData.MaxY - 1; y++)
                    for (short x = 1; x < in3dData.MaxX - 1; x++)
                    {
                        Dimension2D dimension;
                        if (BoardHandler.CheckBoard(in3dData, x, y, z, out dimension))
                            result++;
                    }

            return result;
        }

        /// <summary>
        /// 指定要素の周りが薄板形状かどうかをチェックする
        /// </summary>
        internal static bool CheckBoard(IN3DData in3dData, short x, short y, short z, out Dimension2D dimension)
        {
            dimension = Dimension2D.XY;

            byte value = in3dData.Data[x, y, z];
            if (value != in3dData.MaterialID)
                return false;

            byte[] surround = new byte[27];
            surround[0] = in3dData.Data[x - 1, y - 1, z - 1];
            surround[1] = in3dData.Data[x, y - 1, z - 1];
            surround[2] = in3dData.Data[x + 1, y - 1, z - 1];
            surround[3] = in3dData.Data[x - 1, y, z - 1];
            surround[4] = in3dData.Data[x, y, z - 1];
            surround[5] = in3dData.Data[x + 1, y, z - 1];
            surround[6] = in3dData.Data[x - 1, y + 1, z - 1];
            surround[7] = in3dData.Data[x, y + 1, z - 1];
            surround[8] = in3dData.Data[x + 1, y + 1, z - 1];

            surround[9] = in3dData.Data[x - 1, y - 1, z];
            surround[10] = in3dData.Data[x, y - 1, z];
            surround[11] = in3dData.Data[x + 1, y - 1, z];
            surround[12] = in3dData.Data[x - 1, y, z];
            surround[13] = value;
            surround[14] = in3dData.Data[x + 1, y, z];
            surround[15] = in3dData.Data[x - 1, y + 1, z];
            surround[16] = in3dData.Data[x, y + 1, z];
            surround[17] = in3dData.Data[x + 1, y + 1, z];

            surround[18] = in3dData.Data[x - 1, y - 1, z + 1];
            surround[19] = in3dData.Data[x, y - 1, z + 1];
            surround[20] = in3dData.Data[x + 1, y - 1, z + 1];
            surround[21] = in3dData.Data[x - 1, y, z + 1];
            surround[22] = in3dData.Data[x, y, z + 1];
            surround[23] = in3dData.Data[x + 1, y, z + 1];
            surround[24] = in3dData.Data[x - 1, y + 1, z + 1];
            surround[25] = in3dData.Data[x, y + 1, z + 1];
            surround[26] = in3dData.Data[x + 1, y + 1, z + 1];

            //XY
            if (surround[9] == value && surround[10] == value && surround[11] == value && surround[12] == value &&
                surround[14] == value && surround[15] == value && surround[16] == value && surround[17] == value &&
                surround[0] != value && surround[1] != value && surround[2] != value &&
                surround[3] != value && surround[4] != value && surround[5] != value &&
                surround[6] != value && surround[7] != value && surround[8] != value &&
                surround[18] != value && surround[19] != value && surround[20] != value &&
                surround[21] != value && surround[22] != value && surround[23] != value &&
                surround[24] != value && surround[25] != value && surround[26] != value)
                return false;

            //XZ
            dimension = Dimension2D.XZ;
            if (surround[3] == value && surround[4] == value && surround[5] == value && surround[12] == value &&
                surround[14] == value && surround[21] == value && surround[22] == value && surround[23] == value &&
                surround[0] != value && surround[1] != value && surround[2] != value &&
                surround[9] != value && surround[10] != value && surround[11] != value &&
                surround[18] != value && surround[19] != value && surround[20] != value &&
                surround[6] != value && surround[7] != value && surround[8] != value &&
                surround[15] != value && surround[16] != value && surround[17] != value &&
                surround[24] != value && surround[25] != value && surround[26] != value)
                return true;

            //YZ
            dimension = Dimension2D.YZ;
            if (surround[1] == value && surround[4] == value && surround[7] == value && surround[10] == value &&
                surround[16] == value && surround[19] == value && surround[22] == value && surround[25] == value &&
                surround[0] != value && surround[3] != value && surround[6] != value &&
                surround[9] != value && surround[12] != value && surround[15] != value &&
                surround[18] != value && surround[21] != value && surround[24] != value &&
                surround[2] != value && surround[5] != value && surround[8] != value &&
                surround[11] != value && surround[14] != value && surround[17] != value &&
                surround[20] != value && surround[23] != value && surround[26] != value)
                return true;

            return false;
        }

        /// <summary>
        /// 指定の薄板形状の要素を修正する
        /// </summary>
        /// <param name="materialID">対象材料番号</param>
        /// <param name="b">薄板形状の要素</param>
        /// <param name="units">関連要素の情報</param>
        /// <param name="in3dData">修正対象となる材料データ</param>
        internal static void FixBoard(Board b, Unit[] units, ref IN3DData in3dData, byte fixMaterialID)
        {
            double occSum1 = 0;
            foreach (Unit u in b.FixCandidates1)
                occSum1 += units.First(unit => unit.X == u.X && unit.Y == u.Y && unit.Z == u.Z).OccupiedRatio;

            double occSum2 = 0;
            foreach (Unit u in b.FixCandidates2)
                occSum2 += units.First(unit => unit.X == u.X && unit.Y == u.Y && unit.Z == u.Z).OccupiedRatio;

            if (occSum1 >= occSum2)//補充要素を要素Ⅰに決定
            {
                in3dData.Data[b.FixCandidates1[0].X, b.FixCandidates1[0].Y, b.FixCandidates1[0].Z] = fixMaterialID;
                in3dData.Data[b.FixCandidates1[1].X, b.FixCandidates1[1].Y, b.FixCandidates1[1].Z] = fixMaterialID;
                in3dData.Data[b.FixCandidates1[2].X, b.FixCandidates1[2].Y, b.FixCandidates1[2].Z] = fixMaterialID;
                in3dData.Data[b.FixCandidates1[3].X, b.FixCandidates1[3].Y, b.FixCandidates1[3].Z] = fixMaterialID;
                in3dData.Data[b.FixCandidates1[4].X, b.FixCandidates1[4].Y, b.FixCandidates1[4].Z] = fixMaterialID;
                in3dData.Data[b.FixCandidates1[5].X, b.FixCandidates1[5].Y, b.FixCandidates1[5].Z] = fixMaterialID;
                in3dData.Data[b.FixCandidates1[6].X, b.FixCandidates1[6].Y, b.FixCandidates1[6].Z] = fixMaterialID;
                in3dData.Data[b.FixCandidates1[7].X, b.FixCandidates1[7].Y, b.FixCandidates1[7].Z] = fixMaterialID;
                in3dData.Data[b.FixCandidates1[8].X, b.FixCandidates1[8].Y, b.FixCandidates1[8].Z] = fixMaterialID;
            }
            else//補充要素を要素Ⅱに決定
            {
                in3dData.Data[b.FixCandidates2[0].X, b.FixCandidates2[0].Y, b.FixCandidates2[0].Z] = fixMaterialID;
                in3dData.Data[b.FixCandidates2[1].X, b.FixCandidates2[1].Y, b.FixCandidates2[1].Z] = fixMaterialID;
                in3dData.Data[b.FixCandidates2[2].X, b.FixCandidates2[2].Y, b.FixCandidates2[2].Z] = fixMaterialID;
                in3dData.Data[b.FixCandidates2[3].X, b.FixCandidates2[3].Y, b.FixCandidates2[3].Z] = fixMaterialID;
                in3dData.Data[b.FixCandidates2[4].X, b.FixCandidates2[4].Y, b.FixCandidates2[4].Z] = fixMaterialID;
                in3dData.Data[b.FixCandidates2[5].X, b.FixCandidates2[5].Y, b.FixCandidates2[5].Z] = fixMaterialID;
                in3dData.Data[b.FixCandidates2[6].X, b.FixCandidates2[6].Y, b.FixCandidates2[6].Z] = fixMaterialID;
                in3dData.Data[b.FixCandidates2[7].X, b.FixCandidates2[7].Y, b.FixCandidates2[7].Z] = fixMaterialID;
                in3dData.Data[b.FixCandidates2[8].X, b.FixCandidates2[8].Y, b.FixCandidates2[8].Z] = fixMaterialID;
            }
        }

        #endregion
    }

    /// <summary>
    /// 薄板形状の要素を表すクラス
    /// </summary>
    internal class Board : Unit
    {
        #region Properties

        /// <summary>
        /// 薄板の方向
        /// </summary>
        internal Dimension2D Dimension { get; set; }

        /// <summary>
        /// 補充要素集合Ⅰ
        /// </summary>
        internal Unit[] FixCandidates1
        {
            get
            {
                if (this.Dimension == Dimension2D.XY)
                    return new Unit[9] {
                        new Unit { X = (short)(base.X - 1), Y = (short)(base.Y - 1), Z = (short)(base.Z - 1), },
                        new Unit { X = base.X, Y = (short)(base.Y - 1), Z = (short)(base.Z - 1), },
                        new Unit { X = (short)(base.X + 1), Y = (short)(base.Y - 1), Z = (short)(base.Z - 1), },
                        new Unit { X = (short)(base.X - 1), Y = base.Y, Z = (short)(base.Z - 1), },
                        new Unit { X = base.X, Y = base.Y, Z = (short)(base.Z - 1), },
                        new Unit { X = (short)(base.X + 1), Y = base.Y, Z = (short)(base.Z - 1), },
                        new Unit { X = (short)(base.X - 1), Y = (short)(base.Y + 1), Z = (short)(base.Z - 1), },
                        new Unit { X = base.X, Y = (short)(base.Y + 1), Z = (short)(base.Z - 1), },
                        new Unit { X = (short)(base.X + 1), Y = (short)(base.Y + 1), Z = (short)(base.Z - 1), },
                    };
                else if (this.Dimension == Dimension2D.XZ)
                    return new Unit[9] {
                        new Unit { X = (short)(base.X - 1), Y = (short)(base.Y - 1), Z = (short)(base.Z - 1), },
                        new Unit { X = base.X, Y = (short)(base.Y - 1), Z = (short)(base.Z - 1), },
                        new Unit { X = (short)(base.X + 1), Y = (short)(base.Y - 1), Z = (short)(base.Z - 1), },
                        new Unit { X = (short)(base.X - 1), Y = (short)(base.Y - 1), Z = base.Z, },
                        new Unit { X = base.X, Y = (short)(base.Y - 1), Z = base.Z, },
                        new Unit { X = (short)(base.X + 1), Y = (short)(base.Y - 1), Z = base.Z, },
                        new Unit { X = (short)(base.X - 1), Y = (short)(base.Y - 1), Z = (short)(base.Z + 1), },
                        new Unit { X = base.X, Y = (short)(base.Y - 1), Z = (short)(base.Z + 1), },
                        new Unit { X = (short)(base.X + 1), Y = (short)(base.Y - 1), Z = (short)(base.Z + 1), },
                    };
                else
                    return new Unit[9] {
                        new Unit { X = (short)(base.X - 1), Y = (short)(base.Y - 1), Z = (short)(base.Z - 1), },
                        new Unit { X = (short)(base.X - 1), Y = base.Y, Z = (short)(base.Z - 1), },
                        new Unit { X = (short)(base.X - 1), Y = (short)(base.Y + 1), Z = (short)(base.Z - 1), },
                        new Unit { X = (short)(base.X - 1), Y = (short)(base.Y - 1), Z = base.Z, },
                        new Unit { X = (short)(base.X - 1), Y = base.Y, Z = base.Z, },
                        new Unit { X = (short)(base.X - 1), Y = (short)(base.Y + 1), Z = base.Z, },
                        new Unit { X = (short)(base.X - 1), Y = (short)(base.Y - 1), Z = (short)(base.Z + 1), },
                        new Unit { X = (short)(base.X - 1), Y = base.Y, Z = (short)(base.Z + 1), },
                        new Unit { X = (short)(base.X - 1), Y = (short)(base.Y + 1), Z = (short)(base.Z + 1), },
                    };
            }
        }

        /// <summary>
        /// 補充要素集合Ⅱ
        /// </summary>
        internal Unit[] FixCandidates2
        {
            get
            {
                if (this.Dimension == Dimension2D.XY)
                    return new Unit[9] {
                        new Unit { X = (short)(base.X - 1), Y = (short)(base.Y - 1), Z = (short)(base.Z + 1), },
                        new Unit { X = base.X, Y = (short)(base.Y - 1), Z = (short)(base.Z + 1), },
                        new Unit { X = (short)(base.X + 1), Y = (short)(base.Y - 1), Z = (short)(base.Z + 1), },
                        new Unit { X = (short)(base.X - 1), Y = base.Y, Z = (short)(base.Z + 1), },
                        new Unit { X = base.X, Y = base.Y, Z = (short)(base.Z + 1), },
                        new Unit { X = (short)(base.X + 1), Y = base.Y, Z = (short)(base.Z + 1), },
                        new Unit { X = (short)(base.X - 1), Y = (short)(base.Y + 1), Z = (short)(base.Z + 1), },
                        new Unit { X = base.X, Y = (short)(base.Y + 1), Z = (short)(base.Z + 1), },
                        new Unit { X = (short)(base.X + 1), Y = (short)(base.Y + 1), Z = (short)(base.Z + 1), },
                    };
                else if (this.Dimension == Dimension2D.XZ)
                    return new Unit[9] {
                        new Unit { X = (short)(base.X - 1), Y = (short)(base.Y + 1), Z = (short)(base.Z - 1), },
                        new Unit { X = base.X, Y = (short)(base.Y + 1), Z = (short)(base.Z - 1), },
                        new Unit { X = (short)(base.X + 1), Y = (short)(base.Y + 1), Z = (short)(base.Z - 1), },
                        new Unit { X = (short)(base.X - 1), Y = (short)(base.Y + 1), Z = base.Z, },
                        new Unit { X = base.X, Y = (short)(base.Y + 1), Z = base.Z, },
                        new Unit { X = (short)(base.X + 1), Y = (short)(base.Y + 1), Z = base.Z, },
                        new Unit { X = (short)(base.X - 1), Y = (short)(base.Y + 1), Z = (short)(base.Z + 1), },
                        new Unit { X = base.X, Y = (short)(base.Y + 1), Z = (short)(base.Z + 1), },
                        new Unit { X = (short)(base.X + 1), Y = (short)(base.Y + 1), Z = (short)(base.Z + 1), },
                    };
                else
                    return new Unit[9] {
                        new Unit { X = (short)(base.X + 1), Y = (short)(base.Y - 1), Z = (short)(base.Z - 1), },
                        new Unit { X = (short)(base.X + 1), Y = base.Y, Z = (short)(base.Z - 1), },
                        new Unit { X = (short)(base.X + 1), Y = (short)(base.Y + 1), Z = (short)(base.Z - 1), },
                        new Unit { X = (short)(base.X + 1), Y = (short)(base.Y - 1), Z = base.Z, },
                        new Unit { X = (short)(base.X + 1), Y = base.Y, Z = base.Z, },
                        new Unit { X = (short)(base.X + 1), Y = (short)(base.Y + 1), Z = base.Z, },
                        new Unit { X = (short)(base.X + 1), Y = (short)(base.Y - 1), Z = (short)(base.Z + 1), },
                        new Unit { X = (short)(base.X + 1), Y = base.Y, Z = (short)(base.Z + 1), },
                        new Unit { X = (short)(base.X + 1), Y = (short)(base.Y + 1), Z = (short)(base.Z + 1), },
                    };
            }
        }

        #endregion
    }
}
